/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/
#include "PNGLiteInterface.h"

///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
/// Constructor and Destructor
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
CPNGLiteInterface::CPNGLiteInterface(){
	img = NULL;
	img_size = 0;
	data_size = 0;
	isValid = false;
	
	png_init(0,0); ///< initialize PNGLite
}

CPNGLiteInterface::~CPNGLiteInterface(){
	isValid = false;
	if(img) free(img);
	//if(png.png_data) free(png.png_data);
}
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
/// General
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Get pointer to encoded/decoded image
* @return pointer to image data, NULL if not valid
*/
unsigned char * CPNGLiteInterface::getImagePtr(){
	if(!isValid) return NULL;
	return img;
}

/**
* Get copy of the image data 
* @param *data the pointer where the data is copied to. The memory must be allocated properly 
* @return true, if success
*/
bool CPNGLiteInterface::getImageCopy(unsigned char *data){
	if(!isValid) return false;
	memcpy(data,img,data_size);
	return true;
}

/**
* Saves an image to a file (should be used only with compressed png)
* @param *fname the name of file
* @return true if success
*/
bool CPNGLiteInterface::save(const char *fname){
	if(!isValid) return false;
	FILE *f = fopen(fname,"wb");
	fwrite(img,1,data_size,f);
	fclose(f);
	return true;
}

/**
* Get the size of the image in bytes
* @return size of image, 0 if the image is not valid 
*/
int CPNGLiteInterface::size(){
	if(!isValid) return 0;
	return data_size;
}
/**
* Get the width of the image in pixels
* @return width of image, 0 if the image is not valid 
*/
int CPNGLiteInterface::width(){
	if(!isValid) return 0;
	return png.width;
}

/**
* Get the height of the image in pixels
* @return height of image, 0 if the image is not valid 
*/
int CPNGLiteInterface::height(){
	if(!isValid) return 0;
	return png.height;
}

/**
* Get type (color type) of the image
* @return type (as defined by the pnglite.h), negative if image is not valid
*/
int CPNGLiteInterface::colorType(){
	if(!isValid) return -1;
	return png.color_type;
}

/**
* Get bytes per pixel
* @return bytes per pixel (0 if not valid)
*/
int CPNGLiteInterface::bpp(){
	if(!isValid) return 0;
	return png.bpp;
}

///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
/// Decode
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Decodes a png data from memory
* @param *png_img Pointer to a valid png data
* @return true is decoding was success
*/
bool CPNGLiteInterface::decode(const unsigned char *png_img){
	isValid = false;
	data_size = 0; ///< set data size to zero!
	
	ptrToPng = png_img; ///<store pointer
	
	//int res = png_open_read(&png, png_read_callback, png_img);
	int res = png_open_read(&png, png_read_callback, (void *) this);
	if(res != PNG_NO_ERROR){
		fprintf(stderr,"CPNGLiteInterface::decode::Failed to Read png_img. Error code (%d)\n",res);
		return false;
	}
	//png_print_info(&png);

	checkMemory(png.width*png.height*png.bpp);

	res = png_get_data(&png, img);
	if(res != PNG_NO_ERROR){
		fprintf(stderr,"CPNGLiteInterface::decode::Failed to set data. Error code (%d)\n",res);
		return false;
	}
	isValid=true;
	
	data_size = width()*height()*bpp();
	return true;
}

/**
* Decodes a png data from file
* @param *pngfile filename
* @return true is decoding was success
*/
bool CPNGLiteInterface::decodeFromFile(const char *pngfile){
	FILE * pFile;
  long lSize;
  unsigned char * buffer;
  size_t result;
	
  pFile = fopen ( pngfile , "rb" );
  if (pFile==NULL) {fputs ("File error",stderr); exit (1);}

  // obtain file size:
  fseek (pFile , 0 , SEEK_END);
  lSize = ftell (pFile);
  rewind (pFile);

  // allocate memory to contain the whole file:
  buffer = (unsigned char*) malloc (sizeof(char)*lSize);
  if (buffer == NULL) {fputs ("Memory error",stderr); exit (2);}
	// copy the file into the buffer:
  result = fread (buffer,1,lSize,pFile);
  if (result != lSize) {fputs ("Reading error",stderr); exit (3);}
  /* the whole file is now loaded in the memory buffer. */
  // terminate
  fclose (pFile);
	
	if(!decode(buffer)){
		free(buffer);
		return false;
	}
	
	free(buffer);
	return true;
}

///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
///Encode
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Encode from memory 
* @param *data The image data to be compressed
* @param int w width of the image
* @param int h height of the data
* @param color mode of the data. Defined by pnglite.h in a following manner:
*enum
*{
*	PNG_GREYSCALE			= 0,
*	PNG_TRUECOLOR			= 2,
*	PNG_INDEXED				= 3,
*	PNG_GREYSCALE_ALPHA		= 4,
*	PNG_TRUECOLOR_ALPHA		= 6
*};
* This interface supports at least the two first ones (others are sophisticated guesses)
* @return true on success
*/
bool CPNGLiteInterface::encode(unsigned char *data, int w, int h, int color){
	char depth  = 8; ///< assuming allways depth of eight bits
	data_size = 0;
	isValid = false;
	int bpp;
	
	if(color == PNG_GREYSCALE) bpp=1;
	else if(color == PNG_TRUECOLOR) bpp=3;
	else if(color == PNG_INDEXED) bpp=3; ///No idea...
	else if(color == PNG_GREYSCALE_ALPHA) bpp=2;
	else if(color == PNG_TRUECOLOR_ALPHA) bpp=4;
	checkMemory(bpp*w*h);
	
	int res = png_open_write(&png, png_write_callback, (void *) this);
	if(res != PNG_NO_ERROR){
		fprintf(stderr,"CPNGLiteInterface::encode::Failed to open. Error code (%d)\n",res);
		return false;
	}
	png.compression_method = 0;
	res = png_set_data(&png,  w, h, depth, color, data);
	if(res != PNG_NO_ERROR){
		fprintf(stderr,"CPNGLiteInterface::encode::Failed to set data. Error code (%d)\n",res);
		return false;
	}
	isValid = true;
	return true;
}

///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
/// PRIVATES
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
void CPNGLiteInterface::checkMemory(unsigned int size){
	if(img_size<size){
		if(img) free(img);
		img = (unsigned char *) malloc(size);
		img_size = size;
	}
}

///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
/// Debug
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
#ifdef PNG_LITE_INTERFACE_DEBUG
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
SDL_Surface *screen; ///< For drawing
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
bool initSDL(int Nx,int Ny){
	if(SDL_Init(SDL_INIT_VIDEO) == -1){
		fprintf(stderr,"Failed to initialize SDL Video: %s\n", SDL_GetError());
		exit(1);
	}
  // Register cleanup function for SDL
	atexit(SDL_Quit);
  
  // Initialize SDL-Video in mode 800x600, 32bpp, & some flags.
	if ((screen = SDL_SetVideoMode(Nx, Ny, 32, SDL_HWSURFACE|SDL_DOUBLEBUF|SDL_RESIZABLE)) == NULL) {
				 fprintf(stderr, "Unable to set video mode for SDL Context: %s\n", SDL_GetError());
				 abort();
	}
	 // Clear the screen
	SDL_FillRect(screen, NULL, 0);
  // Set imporant info :)
	SDL_WM_SetCaption("PNG TESTER - 2008","PNG TESTER");
}
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
void displayImage(unsigned char *data, int w, int h, int bytesperpixel){
	initSDL(w,h);
	SDL_Event event;
	
	bool run=true; int cnt  = 0;
	while(run){
		while (SDL_PollEvent(&event)) {
			switch (event.type) {
				case SDL_QUIT: {run = false;break;}
				case SDL_KEYDOWN: {
					switch (event.key.keysym.sym) {
						case SDLK_UP: break;
						case SDLK_DOWN:break;
						case SDLK_q:{run = false;break;}
						case SDLK_ESCAPE:{run = false; exit(1); break;}
						case SDLK_BACKSPACE: break;
						case SDLK_s:{ break;}
						default:break;
					}///<switch (event.key.keysym.sym)
				break;
				}///<case SDL_KEYDOWN:
				case SDL_MOUSEBUTTONDOWN:{
					printf("Mouse button %d pressed at (%d,%d)\n",event.button.button, event.button.x, event.button.y);
					break;
				}
				default:break;
			}///<switch (event.type)
		}///while SDL_PollEvent(&event)
	
		if(cnt<2){
			cnt++;
			for(int i=0;i<w*h;i++){
				switch(bytesperpixel){
					case 1:
						pixelRGBA(screen, i%w, i/w, data[i], data[i], data[i],255);
						break;
					case 3:
						pixelRGBA(screen, i%w, i/w, data[3*i], data[3*i+1], data[3*i+2],255);
						break;
					case 4:
						pixelRGBA(screen, i%w, i/w, data[4*i], data[4*i+1], data[4*i+2],data[4*i+3]);
						break;
					default:
						pixelRGBA(screen, i%w, i/w, data[bytesperpixel*i], 0, 0,255);
						break;
				}
			}
		}
		SDL_Flip(screen);
		usleep(1000*100);
	}///<While(run)
	SDL_FreeSurface(screen);
	screen=NULL;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////


int main(int argc, char *argv[]){
	const char *filename;
	CPNGLiteInterface PNG;
	
	if(argc<1){
		fprintf(stderr,"Give file name as input\n");
		exit(1);
	}
	filename = argv[1];
	
	if(!PNG.decodeFromFile(filename)){
		fprintf(stderr,"Failed to encode '%s'\n",filename);
		exit(1);
	}
	
	PNG.print();
	fprintf(stderr,"Reported Size=%d",PNG.size());
	
	///Encode the same image
	CPNGLiteInterface toPNG;
	toPNG.encode(PNG.getImagePtr(), PNG.width(),PNG.height(), PNG.colorType());
	toPNG.save("test.png");
	
	///display the image
	initSDL(PNG.width(),PNG.height());
	displayImage(PNG.getImagePtr(),PNG.width(),PNG.height(),PNG.bpp());
	
	return 0;
}
#endif

